﻿namespace Webinator.Impls.WebDriver
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using Interfaces;
    using OpenQA.Selenium;

    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Reviewed. Suppression is OK here.")]
    internal class WebDriverHighlighter : IHighlighter
    {
        /// <summary>
        /// The core
        /// </summary>
        private readonly ICore _core;

        /// <summary>
        /// Initializes a new instance of the <see cref="WebDriverHighlighter"/> class.
        /// </summary>
        /// <param name="core">
        /// The core.
        /// </param>
        public WebDriverHighlighter(ICore core)
        {
            _core = core;
        }

        public void Highlight(Locator locator, bool reset)
        {
            IWebElement element;
            try
            {
                element = (IWebElement)_core.FindElement(locator);
            }
            catch (Exception)
            {
                return;
            }

            HighlightElement(element, reset);
        }

        public void HighlightElement(object element, bool reset = false)
        {
            var internetExplorer = _core.Config.Browser == Config.AvailableBrowsers.InternetExplorer;

            if (!_core.Config.HighlightElements) return;
            try
            {
                var elementType = (string)((IJavaScriptExecutor)_core.GetCore()).ExecuteScript("return arguments[0].getAttribute('type');", element);
                var doWrap = elementType == "radio" || elementType == "checkbox";

                if (!reset)
                {
                    // Wrap the element in a highlighted span
                    if (doWrap)
                    {
                        if (internetExplorer)
                        {
                            ((IJavaScriptExecutor)_core.GetCore()).ExecuteScript(
                                @"{var wrapper=document.createElement('span'); " +
                                string.Format(@"wrapper.style.cssText='{0}';", _core.Config.HighlightStyle) +
                                @"wrapper.id='webinatorHighlightWrapper'; " +
                                @"arguments[0].parentNode.insertBefore(wrapper, arguments[0]);" +
                                @"wrapper.appendChild(arguments[0]);} ",
                                element);
                        }
                        else
                        {
                            ((IJavaScriptExecutor)_core.GetCore()).ExecuteScript(
                                @"{var wrapper=document.createElement('span'); " +
                                string.Format(@"wrapper.setAttribute('style','{0}');", _core.Config.HighlightStyle) +
                                @"wrapper.id='webinatorHighlightWrapper'; " +
                                @"arguments[0].parentNode.insertBefore(wrapper, arguments[0]);" +
                                @"wrapper.appendChild(arguments[0]);} ", 
                                element);
                        }
                    }
                    else
                    {
                        // Get the current styling
                        var stylePrevious = (string)((IJavaScriptExecutor)_core.GetCore()).ExecuteScript(
                            internetExplorer
                                ? "return arguments[0].style.cssText;"
                                : "return arguments[0].getAttribute('style');",
                            element) ?? string.Empty;

                        // Store it in a custom attribute
                        ((IJavaScriptExecutor)_core.GetCore()).ExecuteScript(
                            string.Format("arguments[0].setAttribute('webinatorOldStyle','{0}');", stylePrevious), element);
                        
                        // Set the new style
                        ((IJavaScriptExecutor)_core.GetCore()).ExecuteScript(
                            internetExplorer
                            ? string.Format("arguments[0].style.cssText='{0}';", _core.Config.HighlightStyle)
                            : string.Format("arguments[0].setAttribute('style','{0}');", _core.Config.HighlightStyle), 
                            element);

                        // Reset the old style after timeout
                        //((IJavaScriptExecutor)_core.GetCore()).ExecuteScript(
                        //    internetExplorer
                        //    ? string.Format(@"window.setTimeout(function() {{ arguments[0].style.cssText='{0}';  }}, 1000);", stylePrevious)
                        //    : string.Format(@"window.setTimeout(function() {{ arguments[0].setAttribute('style','{0}');  }}, 1000);", stylePrevious),
                        //    element);
                    }
                }
                else
                {
                    // Unwrap the element to remove the higlight
                    if (doWrap)
                    {
                        ((IJavaScriptExecutor)_core.GetCore()).ExecuteScript(
                            @"{var original=document.getElementById('webinatorHighlightWrapper');" +
                            @"var child = original.firstChild;" +
                            @"var parent = original.parentNode;" +
                            @"parent.insertBefore(child, original);" +
                            @"parent.removeChild(original);}");
                    }
                    else
                    {
                        // Get the previous style
                        var stylePrevious = (string)((IJavaScriptExecutor)_core.GetCore()).ExecuteScript("return arguments[0].getAttribute('webinatorOldStyle');", element);

                        // Remove the custom attribute
                        ((IJavaScriptExecutor)_core.GetCore()).ExecuteScript("arguments[0].removeAttribute('webinatorOldStyle');", element);

                        // Set the style to the previous style, or remove the style attribute entirely
                        if (string.IsNullOrEmpty(stylePrevious))
                        {
                            ((IJavaScriptExecutor)_core.GetCore()).ExecuteScript("arguments[0].removeAttribute('style');", element);
                        }
                        else
                        {
                            ((IJavaScriptExecutor)_core.GetCore()).ExecuteScript(
                                internetExplorer
                                ? string.Format("arguments[0].style.cssText='{0}'", stylePrevious)
                                : string.Format("arguments[0].setAttribute('style','{0}');", stylePrevious), 
                                element);
                        }
                    }
                }
            }
            catch
            {
                // We don't really care if the highlight operation failed, so let's just move on
            }
        }
    }
}
